package euplink

import (
	"common/connect_mgr"
	"common/encryp/path"
	"common/kms"
	"common/macaroon"
	"common/pb"
	"common/report"
	rpc2 "common/rpc"
	"context"
	"euplink/center_rpc"
	"euplink/config"
	"euplink/download"
	"euplink/encrypt_key"
	"fmt"
	"github.com/wuzfei/go-helper/slices"
	"go.uber.org/zap"
	"io"
)
import "euplink/upload"

type Project struct {
	log        *zap.Logger
	apiKey     *macaroon.APIKey
	config     config.Config
	centerRpc  *center_rpc.CenterRpc
	dialer     *rpc2.TcpDialer
	encryptKey encrypt_key.EncryptKey
	reporter   *report.Reporter
	uploader   *upload.Uploader
	downloader *download.Downloader
}

func NewProject(ctx context.Context, cfg config.Config, log *zap.Logger) (*Project, error) {
	apiKey, err := macaroon.ParseAPIKey(cfg.ApiKey)
	if err != nil {
		return nil, fmt.Errorf("apikey 解析错误:%s %s", cfg.ApiKey, err)
	}
	prj := &Project{
		config: cfg,
		log:    log,
		apiKey: apiKey,
		dialer: rpc2.NewTcpDialer(false),
	}
	//获取加密key的生成与解码
	if cfg.UseKms {
		prj.encryptKey = encrypt_key.NewKmsEncrypt(kms.NewClient(&cfg.Kms), apiKey)
	} else {
		prj.encryptKey = encrypt_key.NewDefaultEncrypt(apiKey)
	}

	//上报
	if cfg.UseReport {
		prj.reporter = report.NewReporter(&cfg.Report)
	}
	prj.centerRpc = center_rpc.NewClient(ctx, cfg.CenterUrl, apiKey, prj.dialer, log.Named("centerRpc"))
	//if cfg.UseP2P {
	//	if err := p2p.StartP2PListen(ctx, cfg.ApiKey.KeyId(), true); err != nil {
	//		return nil, err
	//	}
	//}
	return prj, nil
}

func (project *Project) GetUploadProgress() int {
	if project.uploader == nil {
		return 0
	}
	return project.uploader.GetPercent()
}
func (project *Project) GetDownloadProgress() int {
	if project.downloader == nil {
		return 0
	}
	return project.downloader.GetPercent()
}

func (project *Project) UploadObject(ctx context.Context, bucket, key string, inReader io.Reader, inline bool, plainSize int64, attrs map[string]string) error {
	project.uploader = upload.NewUploader(
		project.apiKey,
		project.config.MaxSegment,
		project.dialer,
		project.centerRpc,
		project.encryptKey,
		project.log,
	)
	if inline {
		return project.uploader.UploadInline(ctx, bucket, key, inReader)
	} else {
		return project.uploader.Upload(ctx, bucket, key, inReader, plainSize, attrs)
	}
}

// DownloadObject
// 下载文件输出调整成 io.Writer wuxin-23.11.22
func (project *Project) DownloadObject(ctx context.Context, bucket, key string, output io.Writer) error {
	project.downloader = download.NewDownloader(
		project.apiKey,
		project.centerRpc,
		project.dialer,
		project.encryptKey,
		project.log)
	return project.downloader.Download(ctx, bucket, key, 0, 0, output)
}

// DownloadObjectRange 下载一部分文件
// offset： 从这个位置开始下载
// limit： 下载总长度，为0则一直下载到文件末尾
func (project *Project) DownloadObjectRange(ctx context.Context, bucket, key string, offset, limit int64, output io.Writer) error {
	project.downloader = download.NewDownloader(
		project.apiKey,
		project.centerRpc,
		project.dialer,
		project.encryptKey,
		project.log)
	return project.downloader.Download(ctx, bucket, key, offset, limit, output)
}

type ListObjectItem struct {
	ObjectKey string
	Size      int64
}

func (project *Project) ListObject(ctx context.Context, bucket string, pre string, cursor string) (data []ListObjectItem, err error) {
	//centerRpcClient := rpc.CenterRpc{ad: project.config.CenterUrl, ApiKey: project.apiKey}
	opts := center_rpc.ListObjectOpts{}
	if pre != "" {
		opts.EncryptedPrefix, _, _ = path.EncPath(pre, project.apiKey.RootKey())
	}
	if cursor != "" {
		opts.Cursor, _, _ = path.EncPath(cursor, project.apiKey.RootKey())
	}
	items, err := project.centerRpc.ListObjects(ctx, bucket, opts)
	if err != nil || len(items) == 0 {
		return
	}
	data = slices.Map(items, func(v *pb.ObjectListItem, k int) ListObjectItem {
		objectKey, _err := path.DecPath(v.EncryptedObjectKey, project.apiKey.RootKey())
		if _err != nil {
			fmt.Println("objectkey 解开失败", v.EncryptedObjectKey, _err)
		}
		return ListObjectItem{
			ObjectKey: objectKey,
			Size:      v.PlainSize,
		}
	})
	return
}

func (project *Project) DetailObject(ctx context.Context, bucket string, key string) (*pb.ObjectDetailResponse, error) {
	encrypKey, _, err := path.EncPath(key, project.apiKey.RootKey())
	if err != nil {
		return nil, err
	}
	return project.centerRpc.DetailObject(ctx, bucket, encrypKey)
}

func (project *Project) DeleteObject(ctx context.Context, bucket string, key string) error {
	encrypKey, _, err := path.EncPath(key, project.apiKey.RootKey())
	if err != nil {
		return err
	}
	return project.centerRpc.DeleteObject(ctx, bucket, encrypKey)
}

func (project *Project) StartUpdateNodeServer(ctx context.Context) {
	connect_mgr.SetUpdateHandle(project.centerRpc)
	connect_mgr.SetDebugDevice(project.config.DebugDevice)

	//go connect_mgr.StartConnectServer(ctx)
}
